#!/usr/bin/env ruby

# Copyright (c) 2021-2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

include_relative 'common.irt'

function(:IrtocTestCfg, params: {"buf" => 'ptr', "size" => 'u64'}, mode: [:Native]) {
    if Options.arch == :arm32
      Intrinsic(:UNREACHABLE).Terminator.void
      ReturnVoid().void
      next
    end

    i0 := 0
    r0 := 0
    While((i := (i_phi := Phi(i0, i1).u64)), size).NE {
        res := WhilePhi(r0, phi2).u64

        offset := Mul(i, 8).u64
        value := Load(buf, offset).u64
        If(And(i, 1).u64, 0).EQ {
            If(And(value, 1).u64, 0).EQ {
                r1 := Add(res, 2).u64
                v0 := Add(value, 2).u64
                Store(buf, offset, v0).u64
            } Else {
                r2 := Add(res, 1).u64
                v1 := Add(value, 1).u64
                Store(buf, offset, v1).u64
            }
            phi1 := Phi(r1, r2).u64
        } Else {
            r3 := Sub(res, 1).u64
            v2 := Sub(value, 1).u64
            Store(buf, offset, v2).u64
        }
        phi2 := Phi(phi1, r3).u64
        i1 := Add(i, 1).u64
    }
    Return(res).u64
}

function(:IrtocTestCfgLabels, params: {"buf" => 'ptr', "size" => 'u64'}, mode: [:Native]) {
    if Options.arch == :arm32
      Intrinsic(:UNREACHABLE).Terminator.void
      ReturnVoid().void
      next
    end
    i0 := 0
    r0 := 0

    Label(:Head)
    i_phi := Phi(i0, i1).u64
    res := Phi(r0, phi2).u64
    i := i_phi
    If(i, size).EQ.Unlikely {
      Goto(:Exit)
    }

    offset := Mul(i, 8).u64
    value := Load(buf, offset).u64

    If(And(i, 1).u64, 0).NE { Goto(:Else1) }
        If(And(value, 1).u64, 0).NE { Goto(:Else2) }
            r1 := Add(res, 2).u64
            v0 := Add(value, 2).u64
            Store(buf, offset, v0).u64
            Goto(:IfCont2)
        Label(:Else2)
            r2 := Add(res, 1).u64
            v1 := Add(value, 1).u64
            Store(buf, offset, v1).u64
        Label(:IfCont2)
        phi1 := Phi(r1, r2).u64
        Goto(:IfCont1)
    Label(:Else1)
        r3 := Sub(res, 1).u64
        v2 := Sub(value, 1).u64
        Store(buf, offset, v2).u64
    Label(:IfCont1)
    phi2 := Phi(phi1, r3).u64
    i1 := Add(i, 1).u64
    Goto(:Head)
    Label(:Exit)
    Return(res).u64
}

function(:IrtocTestAddValues, params: {a: 'i64', b: 'i64'}, mode: [:Native]) {
    v := Add(a, b).i64
    Return(v).i64
}

function(:IrtocTestIncMaxValue, params: {a: 'u64', b: 'u64'}, mode: [:Native]) {
    If(a, b).GE {
        v1 := Add(a, 1).u64
    }
    Else {
        v2 := Add(b, 1).u64
    }
    phi := Phi(v1, v2).u64
    Return(phi).u64
}

function(:IrtocTestIncMaxValueLabels, params: {a: 'u64', b: 'u64'}, mode: [:Native]) {
    If(a, b).GE {
        Goto(:l1)
    } Else {
        Goto(:l2)
    }
Label(:l1)
    v1 := Add(a, 1).u64
    Goto(:l3)
Label(:l2)
    v2 := Add(b, 1).u64
Label(:l3)
    Return(Phi(v1, v2).u64).u64
}

function(:IrtocTestSeqLabels, params: {a: 'u64'}, mode: [:Native]) {
    If(a, 10).LE {
       ret1 := AddI(a).Imm(1).u64
       Goto(:end)
    }
    If(a, 100).LE {
       ret2 := AddI(a).Imm(2).u64
       Goto(:end)
    }
    ret3 := AddI(a).Imm(3).u64
Label(:end)
    Return(Phi(ret1, ret2, ret3).u64).u64
}

# Return `TestCall(TestCall(n) * n)`
function(:IrtocTestRelocations, params: {"n" => 'u32'}, mode: [:Native]) {
    res := Call(n).Method("TestCall").u32
    res := Mul(res, n).u32
    res := Call(res).Method("TestCall").u32
    Return(res).u32
}

function(:IrtocTestRelocations2,
            params: {a0: 'word', a1: 'word', f0: 'f64', a2: 'word', a3: 'word', a4: 'word', f1: 'f64', f2: 'f64', a5: 'word', a6: 'word', f3: 'f64', a7: 'word', a8: 'word', a9: 'word', f4: 'f64'}, mode: [:Native]) {
    fres := Call(f0).Method("IncrementFloat").f64
    fres := Add(fres, Call(f1).Method("IncrementFloat").f64).f64
    fres := Add(fres, Call(f2).Method("IncrementFloat").f64).f64
    fres := Add(fres, Call(f3).Method("IncrementFloat").f64).f64
    fres := Add(fres, Call(f4).Method("IncrementFloat").f64).f64
    ares := Call(a0).Method("IncrementInt").word
    ares := Add(ares, Call(a1).Method("IncrementInt").word).word
    ares := Add(ares, Call(a2).Method("IncrementInt").word).word
    ares := Add(ares, Call(a3).Method("IncrementInt").word).word
    ares := Add(ares, Call(a4).Method("IncrementInt").word).word
    ares := Add(ares, Call(a5).Method("IncrementInt").word).word
    ares := Add(ares, Call(a6).Method("IncrementInt").word).word
    ares := Add(ares, Call(a7).Method("IncrementInt").word).word
    ares := Add(ares, Call(a8).Method("IncrementInt").word).word
    ares := Add(ares, Call(a9).Method("IncrementInt").word).word

    res := Add(ares, Cast(fres).SrcType("DataType::FLOAT64").word).word
    Return(res).word
}

# Return sum of all numbers from 0 to `n`
# Fixed issue: #6890
# Problem: `Goto` right after `If` block with only `Goto` inside leads to wrong graph
function(:IrtocTestLabels, params: {n: 'word'}, mode: [:Native]) {
    res_init := 0
    i_init := 0
Label(:Header)
    res := Phi(res_init, res_next).word
    i := Phi(i_init, i_next).word

    res_next := Add(res, i).word
    i_next := AddI(i).Imm(1).word
    If(i_next, n).GT.Unlikely.b {
        Goto(:Tail)
    }
    Goto(:Header)
Label(:Tail)
    Return(res_next).word
}

function(:IrtocTestReturnBeforeLabel, params: {n: 'word'}, mode: [:Native]) {
    If(n, 100).GT.b {
        Goto(:Exit1)
    } Else {
        Goto(:Exit2)
    }
Label(:Exit1)
    Return(1).word
Label(:Exit2)
    Return(2).word
}
